(function($, rf) {

	rf.ui = rf.ui || {};

	var defaultOptions = {
		penColor : "#191970",
		penWidth : 3.0,
		imageFormat: "png"
	};
	var constants = {
		dataVersion : 1
	};

	var SignpadContext = function(signpad) {
		this.signpad = signpad;
	};
	var internal = {
		
		processEventLocation : function(event, $canvas) {
			var x, y, offsetTop, offsetLeft, offsetObject;

			if ($.browser.msie && parseInt($.browser.version) === 6) {
				// IE6 doesn't calculate the offset properties correctly
				offsetObject = $canvas[0];
				offsetLeft = 0;
				offsetTop = 0;

				if (offsetObject.offsetParent) {
					do {
						offsetLeft += offsetObject.offsetLeft;
						offsetTop += offsetObject.offsetTop;
						offsetObject = offsetObject.offsetParent;
					} while (offsetObject);
				}
			} else {
				offsetTop = $canvas.offset().top;
				offsetLeft = $canvas.offset().left;
			}

			if (event.originalEvent.touches) {
				x = event.originalEvent.touches[0].pageX - offsetLeft;
				y = event.originalEvent.touches[0].pageY - offsetTop;
			} else {
				x = event.pageX - offsetLeft;
				y = event.pageY - offsetTop;
			}

			return {
				x : x,
				y : y
			};
		},
		calculateBoundaryCrossing : function(location, data) {
			var intersect, returnValue, borders, i;

			returnValue = {
				x : location.x,
				y : location.y
			};

			borders = [
					[ 0, 0, data.canvasWidth, 0 ],
					[ 0, 0, 0, data.canvasHeight ],
					[ 0, data.canvasHeight, data.canvasWidth, data.canvasHeight ],
					[ data.canvasWidth, 0, data.canvasWidth, data.canvasHeight ] ];

			for (i = 0; i < 4; i++) {
				intersect = internal.computeLineIntersection(borders[i][0],
						borders[i][1], borders[i][2], borders[i][3],
						data.lastLocation.x, data.lastLocation.y, location.x,
						location.y);

				if (intersect.status === "intersect") {
					returnValue.x = intersect.x;
					returnValue.y = intersect.y;
					break;
				} else if (intersect.status === "collinear") {
					break;
				}
			}

			// Make sure returned value is within bounds (correcting floating
			// point error)

			if (returnValue.x < 0) {
				returnValue.x = 0;
			} else if (returnValue.x > data.canvasWidth) {
				returnValue.x = data.canvasWidth;
			}
			if (returnValue.y < 0) {
				returnValue.y = 0;
			} else if (returnValue.y > data.canvasHeight) {
				returnValue.y = data.canvasHeight;
			}

			return returnValue;
		},
		computeLineIntersection : function(x1, y1, x2, y2, x3, y3, x4, y4) {
			// Implementation follows
			// http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/

			var n1, n2, d, r1, r2;

			n1 = ((x4 - x3) * (y1 - y3)) - ((y4 - y3) * (x1 - x3));
			n2 = ((x2 - x1) * (y1 - y3)) - ((y2 - y1) * (x1 - x3));
			d = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1));

			if (d === 0.0) {
				if ((n1 === 0.0) && (n2 === 0.0)) {
					return {
						status : "collinear",
						x : null,
						y : null
					};
				} else {
					return {
						status : "disjoint",
						x : null,
						y : null
					};
				}
			} else {
				r1 = n1 / d;
				r2 = n2 / d;

				if ((r1 >= 0) && (r1 <= 1) && (r2 >= 0) && (r2 <= 1)) {
					return {
						status : "intersect",
						x : x1 + r1 * (x2 - x1),
						y : y1 + r1 * (y2 - y1)
					};
				} else {
					return {
						status : "disjoint",
						x : null,
						y : null
					};
				}
			}
		},
		handleMouseMove : function(event) {

		}
	};

	rf.ui.Signpad = function(componentId, options) {
		$super.constructor.call(this, componentId);
		this.namespace = "." + rf.Event.createNamespace(this.name, componentId);
		this.options = $.extend(this.options, defaultOptions, options);
		
		// Attach data storage to this object
		$this = $(rf.getDomElement(componentId));
		if (!$this.data("signaturePanel")) {
			$this.data("signaturePanel", {
				dataVersion : constants.dataVersion,
				clickstream : [],
				drawState : "none",
				canvasHeight : 0,
				canvasWidth : 0,
				firstTime : null,
				settings : {},
				lastLocation : {},
				emulatedCanvas : false,
				imageFormat: "png",
				getSignatureData : function() {
					return {
						dataVersion : this.dataVersion,
						clickstream : this.clickstream,
						penColor : this.settings.penColor,
						penWidth : this.settings.penWidth,
						canvasHeight : this.canvasHeight,
						canvasWidth : this.canvasWidth,
						imageFormat: this.settings.imageFormat 
					};
				},
				clearSignature : function() {
					this.clickstream = [];
					this.drawState = "none";
					this.havePath = false;
					this.firstTime = null;
				}
			});
		}
		this.data = $this.data("signaturePanel");

		// Apply user-supplied options
		if (this.options) {
			$.extend(this.data.settings, defaultOptions, options);
		}

		// Create user interface elements
		$this.empty();
		
	//	$this.append("<input id=\""+this.id+":CLEARBTN\" name=\""+this.id+":CLEARBTN\" type=\"image\" src=\""+this.data.settings.clearButtonIcon+"\" class=\"ricche-signpad-clear-btn\"/>");
		
		$this
				.append("<div id=\""
						+ componentId
						+ ":signature-panel-wrapper\" style=\"position: relative; height: "
						+ $this.height() + "px; width: " + $this.width()
						+ "px;\"></div>");
		$wrapper = $(rf.getDomElement(componentId + ":signature-panel-wrapper"));

		// We're using native DOM methods to work around some quirks with
		// ExplorerCanvas when dynamically
		// creating canvas elements.
		this.canvas = document.createElement('canvas');
		$wrapper.append(this.canvas);

		this.canvas.setAttribute("id", componentId + "Canvas");
		this.canvas.setAttribute("width", $this.width());
		this.canvas.setAttribute("height", $this.height());

		if (typeof G_vmlCanvasManager !== "undefined") {
			// initialize IE Canvas emulation
			this.canvas = G_vmlCanvasManager.initElement(canvas);
			this.data.emulatedCanvas = true;
		}

		$canvas = $(this.canvas);
		this.context = this.canvas.getContext('2d');

		this.data.canvasHeight = this.canvas.height;
		this.data.canvasWidth = this.canvas.width;

		this.context.lineWidth = this.data.settings.penWidth;
		this.context.strokeStyle = this.data.settings.penColor;
		this.context.lineCap = "round";
		this.context.lineJoin = "round";
		this.context.fillStyle = "none";
		this.signpadContext = new SignpadContext(this);
		// $canvas.bind("mousedown.signaturePanel
		// touchstart.signaturePanel",$.proxy(this.bindCanvas,this));
		
		// Attach event handlers
		var handlers = {};
		handlers["mousedown.signaturePanel touchstart.signaturePanel"] = this.touchstart;
		rf.Event.bind(this.canvas, handlers, this);
		
		handlers = {};
		handlers["click.signaturePanel"] = this.clearSignature;
		rf.Event.bind(rf.getDomElement(this.id+":CLEARBTN"), handlers, this);
		
		// $this.find(".signature-panel-clear").bind("click.signaturePanel",
		// function () {
		// internal.clearSignature($canvas[0], context, data);
		// return false;
		// });
		//
		// $this.find(".signature-panel-cancel").bind("click.signaturePanel",
		// function () {
		// internal.clearSignature($canvas[0], context, data);
		// if (data.settings.cancelCallback) {
		// data.settings.cancelCallback();
		// }
		// return false;
		// });
		//
		// $this.find(".signature-panel-ok").bind("click.signaturePanel",
		// function () {
		// if (data.settings.okCallback) {
		// data.settings.okCallback(data.getSignatureData());
		// }
		// return false;
		// });

	};
	// Extend component class and add protected methods from parent class to our
	// container
	rf.BaseComponent.extend(rf.ui.Signpad);

	// define super class link
	var $super = rf.ui.Signpad.$super;
	$
			.extend(
					rf.ui.Signpad.prototype,
					{
						name : "Signpad",
						drawClickstreamToCanvas : function(signatureData) {
							if (signatureData != null) {
								$this = $(rf.getDomElement(this.id));
								return $this
										.each(function() {
											var canvas, context, i, scalingFactorX, scalingFactorY, scalingFactor, x, y, $canvas;

											if (signatureData.dataVersion !== 1) {
												throw new Error(
														"Unsupported data version");
											}

											canvas = rf.getDomElement($this
													.attr('id')
													+ "Canvas");
											context = canvas.getContext("2d");
											$canvas = $(canvas);

											

											if ($canvas.width() <= 0) {
												scalingFactorX = 0;
											} else {
												scalingFactorX = $canvas
														.width()
														/ signatureData.canvasWidth;
											}
											if ($canvas.height() <= 0) {
												scalingFactorY = 0;
											} else {
												scalingFactorY = $canvas
														.height()
														/ signatureData.canvasHeight;
											}
											scalingFactor = Math.min(
													scalingFactorX,
													scalingFactorY);

											// render clickstream
											context.lineWidth = signatureData.penWidth
													* scalingFactor;
											context.strokeStyle = signatureData.penColor;
											context.lineCap = "round";
											context.lineJoin = "round";
											context.fillStyle = "none";
											context.beginPath();

											for (i = 0; i < signatureData.clickstream.length; i++) {
												x = signatureData.clickstream[i].x
														* scalingFactor;
												y = signatureData.clickstream[i].y
														* scalingFactor;
												switch (signatureData.clickstream[i].action) {
												case "gestureResume":
												case "gestureStart":
													context.moveTo(x, y);
													break;
												case "gestureContinue":
												case "gestureSuspend":
													context.lineTo(x, y);
													break;
												}
											}
											context.stroke();
											context.closePath();
										})
							}
						},
						handleMouseMove : function(event) {
							var location, t, inBounds, boundaryLocation, lastLocationInBounds;
							$canvas = $(this.canvas);
							t = (new Date).getTime();

							if ((this.data.drawState === "draw")
									|| (this.data.drawState === "suspend")) {
								event.preventDefault();
								location = internal.processEventLocation(event,
										$(this.canvas));
								inBounds = !((location.x < 0)
										|| (location.x > this.data.canvasWidth)
										|| (location.y < 0) || (location.y > this.data.canvasHeight));
								t = t - this.data.firstTime;
							} else {
								// This shouldn't have fired; somehow we missed
								// our chance to
								// unbind. Unbind the event handler now.
								rf.Event.unbind($(document), ".signaturePanel");
								return;
							}

							/*
							 * We're catching a number of tricky cases here.
							 * We're capturing mouse movements even outside the
							 * canvas so that we can maintain a continuous
							 * gesture for the user even if they draw outside
							 * the lines. It's tempting to just record all the
							 * mouse movements even if they're out-of-bounds,
							 * but that really isn't OK, because we're not
							 * showing those captured points to the user. It
							 * would be misleading because we're showing the
							 * signature to the user, and they would reasonably
							 * assume that what we're showing is what they're
							 * approving when they click OK.
							 * 
							 * If we just record the points that occur in
							 * bounds, we get ugly (and bogus) connecting lines
							 * when we cross back into bounds.
							 * 
							 * To get around these things, we calculate the
							 * boundary crossing point and add it to the list.
							 * To indicate that the gesture is still continuing,
							 * we record it as a suspension.
							 */

							lastLocationInBounds = (this.data.drawState === "draw");

							if (lastLocationInBounds) {
								if (inBounds) {
									this.context.lineTo(location.x,
											location.y);
									if (!this.data.emulatedCanvas) {
										// The canvas emulation tends to jitter
										// between drawing
										// calls
										// This makes the effect less pronounced
										this.context.clearRect(0, 0,
												this.data.canvasWidth,
												this.data.canvasHeight);
									}
									this.context.stroke();
									this.data.clickstream.push({
										x : location.x,
										y : location.y,
										t : t,
										action : "gestureContinue"
									});
									this.data.lastLocation = location;
								} else {
									boundaryLocation = internal
											.calculateBoundaryCrossing(
													location, this.data);
									this.context.lineTo(boundaryLocation.x,
											boundaryLocation.y);
									if (!this.data.emulatedCanvas) {
										// The canvas emulation tends to jitter
										// between drawing
										// calls
										// This makes the effect less pronounced
										this.context.clearRect(0, 0,
												this.data.canvasWidth,
												this.data.canvasHeight);
									}
									this.context.stroke();
									this.data.clickstream.push({
										x : boundaryLocation.x,
										y : boundaryLocation.y,
										t : t,
										action : "gestureSuspend"
									});
									this.data.lastLocation = location;
									this.data.drawState = "suspend";
								}
							} else {
								if (inBounds) {
									boundaryLocation = internal
											.calculateBoundaryCrossing(
													location, signpad.data);
									this.context.moveTo(boundaryLocation.x,
											boundaryLocation.y);
									this.data.clickstream.push({
										x : boundaryLocation.x,
										y : boundaryLocation.y,
										t : t,
										action : "gestureResume"
									});
									this.data.lastLocation = location;
									this.data.drawState = "draw";
								} else {
									this.data.lastLocation = location;
								}
							}
						},

						handleMouseUp : function(event) {
							if (this.data.drawState !== "none") {
								event.preventDefault();
								this.data.drawState = "none";
								var input = $(rf.getDomElement(this.id+ ":InputSigns"));
								input.val(JSON.stringify(this.data.getSignatureData()));
							}
							
							rf.Event.unbind($(document), ".signaturePanel");
						},

						touchstart : function(event) {

								var location, t;
	
								t = (new Date).getTime();
								if (!this.data.firstTime) {
									this.data.firstTime = t;
								}
								t = t - this.data.firstTime;
								event.preventDefault();
								location = internal.processEventLocation(event,  $(this.canvas));
								this.data.drawState = "draw";
								if (!this.data.havePath) {
									this.context.beginPath();
									this.data.havePath = true;
								}
								this.context.moveTo(location.x, location.y);
								this.data.lastLocation = location;
								this.data.clickstream.push({x: location.x, y: location.y, t: t, action: "gestureStart"});
	
								// Bind mouseMove and mouseUp handlers
								rf.Event.unbind($(document), ".signaturePanel");
								var handlers = {};
								handlers["mousemove.signaturePanel touchmove.signaturePanel"] = this.handleMouseMove;
								rf.Event.bind($(document), handlers, this);
								var handlers = {};
								handlers["mouseup.signaturePanel touchend.signaturePanel touchcancel.signaturePanel"] = this.handleMouseUp;
								rf.Event.bind($(document), handlers, this);
								// $(document).unbind(".signaturePanel");
								// $(document).bind("mousemove.signaturePanel
								// touchmove.signaturePanel", handleMouseMove);
								// $(document).bind("mouseup.signaturePanel
								// touchend.signaturePanel
								// touchcancel.signaturePanel", handleMouseUp);
	
							
						},
						clearHtmlCanvas : function(canvas, context) {
							context.save();
							context.closePath();
							context.clearRect(0, 0, canvas.width, canvas.height);
							context.restore();
						},
						clearSignature : function(event) {
							this.clearHtmlCanvas(this.canvas, this.context);
							this.data.clearSignature();
							var input = $(rf.getDomElement(this.id+ ":InputSigns"));
							input.val(null);
							return false;
						}
						
					});

})(jQuery, RichFaces);